<template>
  <div class="pdf-marking-out-box" @contextmenu.prevent>
    <div class="pdf-marking-in-box">
      <div class="tool-out-box" :style="{height:`${toolboxHeight}`}">
        <slot name="toolbox" :prop="state.prop" :method="state.method">
          <!--<div class="tool-box">-->
          <div class="tool-box-item search" title="搜索" @click="toggleSearchBoxShow"/>
          <div class="tool-box-item zoomin" title="放大" @click="zoomin"/>
          <div class="tool-box-item zoomout" title="缩小" @click="zoomout"/>
          <div class="tool-box-item rotatecw" title="顺时针旋转" @click="rotateCw"/>
          <div class="tool-box-item rotateccw" title="逆时针旋转" @click="rotateCcw"/>
          <div class="tool-box-item pointertype" v-show="pointerType===POINTERTYPE.text" title="手形工具"
               @click="setPointerType(POINTERTYPE.hand)"/>
          <div class="tool-box-item pointertype active" v-show="pointerType===POINTERTYPE.hand" title="文本选择工具"
               @click="setPointerType(POINTERTYPE.text)"/>
          <div class="tool-box-item scrawl" title="涂鸦" @click="isScrawl=true"/>
          <div class="tool-box-item scrawlshow" v-show="!scrawlShow" title="显示涂鸦" @click="scrawlShow=true"/>
          <div class="tool-box-item scrawlshow active" v-show="scrawlShow" title="隐藏涂鸦"
               @click="scrawlShow=false"/>
          <div class="tool-box-item markshow" v-show="!markShow" title="显示标记" @click="markShow=true"/>
          <div class="tool-box-item markshow active" v-show="markShow" title="隐藏标记" @click="markShow=false"/>
          <!--</div>-->
        </slot>
      </div>
      <div class="container-out-box">
        <div class="container-box" ref="container" @wheel.prevent="handleWheel"
             @contextmenu="exitClip"
             @focus="handleContainerFocus"
             @blur="handleContainerBlur"
             @mousedown.right="handleRightMousedown" :style="containerBoxStyle" tabindex="1">
          <v-stage ref="stage" :config="stageConfig" @contextmenu="$event.evt.preventDefault()">
            <layer-view :data="dataShowList" :style="{cursor:'col-resize'}"/>
          </v-stage>
          <vue-resize-sensor :initial="true" @resize="stageSize=$event"/>
        </div>
        <div class="scrollbar v" ref="vScrollbar" @mousedown.stop v-show="scrollbar.v"
             :style="{width:scrollbarConfig.width||'17px',bottom:scrollbar.h?(scrollbarConfig.height||'17px'):'0'}">
          <div class="scrollbar-thumb" :style="scrollbarVThumbStyle" @mousedown="handleVScollbarMousedown"/>
        </div>
        <div class="scrollbar h" ref="hScrollbar" @mousedown.stop v-show="scrollbar.h"
             :style="{height:scrollbarConfig.height||'17px',right:scrollbar.v?(scrollbarConfig.width||'17px'):'0'}">
          <div class="scrollbar-thumb" :style="scrollbarHThumbStyle" @mousedown="handleHScollbarMousedown"/>
        </div>

        <div class="mask-box" v-if="maskBound.show" @contextmenu="exitClip" @click="exitScrawl">
          <div class="mask-top" :style="{width:`${maskBound.x+maskBound.width}px`,height:`${maskBound.y}px`}"/>
          <div class="mask-right"
               :style="{width:`calc(100% - ${maskBound.x+maskBound.width}px)`,height:`${maskBound.y+maskBound.height}px`}"/>
          <div class="mask-bottom"
               :style="{width:`calc(100% - ${maskBound.x}px)`,height:`calc(100% - ${maskBound.y+maskBound.height}px)`}"/>
          <div class="mask-left" :style="{width:`${maskBound.x}px`,height:`calc(100% - ${maskBound.y}px)`}"/>
        </div>
        <div class="scrawl-tool" ref="scrawlTool"
             :class="{show:isScrawl}"
             :style="{left:`${scrawlToolPosition.x}px`,top:`${scrawlToolPosition.y}px`}">
          <div v-if="isExpandScrawlTool" :class="{active:scrawlType===SCRAWLTYPE.Line}"
               @click="scrawlType=SCRAWLTYPE.Line"
               class="scrawl-tool-item line" title="曲线"/>
          <div v-if="isExpandScrawlTool" :class="{active:scrawlType===SCRAWLTYPE.Rect}"
               @click="scrawlType=SCRAWLTYPE.Rect"
               class="scrawl-tool-item rect" title="矩形"/>
          <div v-if="isExpandScrawlTool" :class="{active:scrawlType===SCRAWLTYPE.Ellipse}"
               @click="scrawlType=SCRAWLTYPE.Ellipse"
               class="scrawl-tool-item ellipse" title="椭圆"/>
          <div v-if="isExpandScrawlTool" :class="{active:scrawlType===SCRAWLTYPE.Arrow}"
               @click="scrawlType=SCRAWLTYPE.Arrow"
               class="scrawl-tool-item arrow" title="箭头"/>
          <div v-if="isExpandScrawlTool" :class="{active:scrawlType===SCRAWLTYPE.Eraser}"
               @click="scrawlType=SCRAWLTYPE.Eraser"
               class="scrawl-tool-item eraser" title="橡皮擦"/>
          <div v-if="isExpandScrawlTool" class="scrawl-tool-item color" title="颜色">
            <input type="color" v-model="brushColor">
          </div>
          <div v-if="isExpandScrawlTool" class="scrawl-tool-item width" title="粗细">
            <select v-model="brushWidth">
              <option :value="10">10</option>
              <option :value="15">15</option>
              <option :value="20">20</option>
              <option :value="30">30</option>
              <option :value="40">40</option>
              <option :value="60">60</option>
              <option :value="80">80</option>
            </select>
          </div>
          <div v-if="isExpandScrawlTool" @click="exitScrawl" class="scrawl-tool-item exit" title="退出"></div>
          <div @click="isExpandScrawlTool=true" class="scrawl-tool-item expand" title="展开工具栏"
               v-if="!isExpandScrawlTool"/>
          <div @click="isExpandScrawlTool=false" class="scrawl-tool-item collapse" title="折叠工具栏"
               v-if="isExpandScrawlTool"/>
        </div>
        <div class="clip-tool" ref="clipTool">
          <div class="clip-tool-item" @click="exitClip">取消</div>
          <div class="clip-tool-item" @click="exitClip(true)">完成</div>
        </div>

        <slot name="searchBox" :prop="state.prop" :method="state.method">
          <div v-show="searchBoxShow" class="marking-search-box"
               :style="{right:`calc(${scrollbarConfig.width||'17px'} + 20px)`}">
            <div class="input">
              <input type="text" placeholder="搜索..." v-model="searchText" :disabled="searchLoading"
                     v-focus="searchBoxShow"
                     @keydown.stop.enter="search(searchText)">
              <span class="search-result">{{seachResultIndex}}/{{searchResultCount}}</span>
            </div>
            <div class="btn search" v-show="!searchLoading" title="搜索" @click="search(searchText)"></div>
            <div class="btn searching" v-show="searchLoading" title="搜索中"></div>
            <div class="btn prev" :class="{disable:searchLoading}" title="上一个" @click="prevSearchResult"></div>
            <div class="btn next" :class="{disable:searchLoading}" title="下一个" @click="nextSearchResult"></div>
            <div class="btn exit" title="关闭搜索栏" @click="exitSearch"></div>
          </div>
        </slot>
      </div>
    </div>
  </div>
</template>

<script>
  import vueResizeSensor from 'vue-resize-sensor'
  import LayerView from "./LayerView"
  import ComponentMixins from "./ComponentMixins"
  import uuid from "uuid/v4"
  import Konva from "konva"
  import VueKonva from "@/components/vue-konva";
  import Hammer from 'hammerjs'

  import {addEventListener, removeEventListener} from '../../../util/eventManager'

  let {vStage} = VueKonva

  Konva.showWarnings = false

  const brushCursor = `url(${require("../../../assets/img/brush.png")}), default`
  const eraserCursor = `url(${require("../../../assets/img/eraser_black.png")}), default`

  export const SCRAWLTYPE = {
    Line: 'Line',
    Rect: 'Rect',
    Arrow: 'Arrow',
    Ellipse: 'Ellipse',
    Eraser: 'Eraser'
  }
  export const POINTERTYPE = {
    hand: 'hand',
    text: 'text'
  }
  export const SCALETYPE = {
    'page-fit': 'page-fit', // 适合页面
    'pageFit': 'page-fit', // 适合页面
    'page-actual': 'page-actual',// 原始大小
    'pageActual': 'page-actual',// 原始大小
    'page-width': 'page-width', //适合页宽
    'pageWidth': 'page-width', //适合页宽
  }

  export default {
    name: "PdfMarking",
    components: {vueResizeSensor, LayerView, vStage},
    mixins: [ComponentMixins],
    data() {
      return {
        SCRAWLTYPE,
        POINTERTYPE,
        SCALETYPE,
        root: true,
        dataList: [],
        markList: [],
        scrawlList: [],
        // layer: null,

        stageSize: {
          width: 0,
          height: 0
        },

        privateData: {
          scaleType: SCALETYPE.pageFit,

          seachResultIndex: 0,
          scrollLeft: 0,
          scrollTop: 0,
          rotation: 0
        },

        imageSize: {width: 2479, height: 3508},
        dx: 0,
        dy: 0,

        isExpandScrawlTool: true,
        isScrawl: false,
        isClip: false,
        clipData: {
          x: 0,
          y: 0,
          width: 1,
          height: 1
        },
        scrawlType: SCRAWLTYPE.Line,
        brushColor: '#ff0000',
        brushWidth: 15,

        scrawlShow: false,
        markShow: false,
        pointerType: POINTERTYPE.text, // hand text
        isOverScrawlTool: false,
        loadArg: null,

        isGrabbing: false,
        searchBoxShow: false,
        searchText: '',
        searchResultList: [],
        searchLoading: false,
        imageGroupBound: {
          x: 0,
          y: 0,
          width: 0,
          height: 0
        },
        loaded: false,
        cursor: '',
        hm: null,
        oldScale: 1,
        autoScrollViewing: null,
        isOpened: false
      }
    },
    watch: {
      isScrawl(val) {
        if (val) {
          this.setScrawlToolPosition()
        } else {
          let scrawlItem = this.scrawlList.find(item => item.pageIndex === this.currPageIndex)
          scrawlItem = JSON.parse(JSON.stringify(scrawlItem || {pageIndex: this.currPageIndex, data: []}))
          this.$emit('scrawl-complete', scrawlItem)
        }
      },
      currPageIndex() {
        if (isNaN(this.currPageIndex) || this.currPageIndex < 0) return
        this.$emit('page-change', this.currPageIndex)
      },
      progress() {
        if (isNaN(this.scrollHeight) || isNaN(this.currPageIndex) || isNaN(this.progress) || this.currPageIndex < 0) return
        this.$emit('progress-change', {
          scrollHeight: this.scrollHeight,
          pageIndex: this.currPageIndex,
          progress: this.progress
        })
      },
      scale() {
        if (isNaN(this.scrollHeight) || isNaN(this.currPageIndex) || isNaN(this.progress) || this.currPageIndex < 0) return
        this.$emit('progress-change', {
          scrollHeight: this.scrollHeight,
          pageIndex: this.currPageIndex,
          progress: this.progress
        })
      },
      rotation() {
        if (isNaN(this.scrollHeight) || isNaN(this.currPageIndex) || isNaN(this.progress) || this.currPageIndex < 0) return
        this.$emit('progress-change', {
          scrollHeight: this.scrollHeight,
          pageIndex: this.currPageIndex,
          progress: this.progress
        })
      },
      async seachResultIndex(newVal, oldVal) {
        if (newVal === 0) return
        let searchResultItem = this.searchResultList[newVal - 1]
        let A = searchResultItem[0]
        let B = searchResultItem[searchResultItem.length - 1]
        if (newVal > oldVal) {
          this.skipByWordPos(`${B.pageIndex}-${B.index}`)
        } else {
          this.skipByWordPos(`${A.pageIndex}-${A.index}`)
        }
        // this.skipPage(A.pageIndex)
      },
      searchBoxShow(val) {
        if (!val) {
          this.searchText = ''
          this.searchResultList = []
          this.seachResultIndex = 0
        }
      },
      'maskBound.show'(val) {
        if (!val) return
        let imageGroup = this.getStage().findOne(`.imageGroup${this.currPageIndex}`)
        let clientRect = imageGroup.getClientRect()
        // console.log(clientRect)
        this.imageGroupBound.x = clientRect.x
        this.imageGroupBound.y = clientRect.y
        this.imageGroupBound.width = clientRect.width
        this.imageGroupBound.height = clientRect.height
      },
      '$refs.scrawlTool'() {
        // console.log('sssssssssss')
      }
    },
    computed: {
      scrawlToolPosition() {
        let x = 0
        let y = 0
        if (this.isOverScrawlTool) {
          x = this.maskBound.x
          y = this.maskBound.y
        } else if (this.loaded) {
          x = this.maskBound.x
          y = this.maskBound.y + this.maskBound.height - this.$refs.scrawlTool.offsetHeight
        }
        return {x, y}
      },
      searchResultCount() {
        return this.searchResultList.length
      },
      seachResultIndex: {
        get() {
          return this.privateData.seachResultIndex
        },
        set(val) {
          if (this.maskBound.show) return
          if (!this.searchResultCount) val = 0
          else if (val < 1) val = this.searchResultCount
          else if (val > this.searchResultCount) val = 1
          this.privateData.seachResultIndex = val
        }
      },
      containerBoxStyle() {
        let width = '100%'
        let height = '100%'
        if (this.scrollbar.h) height = `calc(100% - ${this.scrollbarConfig.height || '17px'})`

        if (this.scrollbar.v) width = `calc(100% - ${this.scrollbarConfig.width || '17px'})`

        let cursor = 'default'
        if (this.isClip && this.cursor) switch (this.cursor) {
          case 'left-top':
          case 'right-bottom':
            cursor = 'se-resize'
            break
          case 'left-middle':
          case 'right-middle':
            cursor = 'ew-resize'
            break
          case 'left-bottom':
          case 'right-top':
            cursor = 'sw-resize'
            break
          case 'center-top':
          case 'center-bottom':
            cursor = 's-resize'
            break
        } else if (this.cursor) cursor = this.cursor
        else if (this.isGrabbing) cursor = 'grabbing'
        else if (this.isScrawl) {
          if (this.scrawlType === SCRAWLTYPE.Eraser) cursor = eraserCursor
          else cursor = brushCursor
        } else if (this.isClip) cursor = 'crosshair'
        else if (this.pointerType === POINTERTYPE.hand) cursor = 'grab'
        else if (this.pointerType === POINTERTYPE.text) cursor = 'text'

        return {
          width,
          height,
          cursor
        }
      },
      scrollbar() {
        let scrollbar = {
          v: this.scrollHeight > 0,
          h: this.scrollWidth > 0
        }
        this.scrollLeft = this.scrollLeft
        this.scrollTop = this.scrollTop
        return scrollbar
      },
      maskBound() {
        // let width = this.imageSize.width * this.scale
        //
        // if (width > this.stageSize.width) width = this.stageSize.width
        //
        // let newY = 0
        // let newH = this.imageSize.height * this.scale
        //
        // let deltaY = (this.imageSize.height * this.currPageIndex - this.scrollTop) * this.scale
        //
        // newY = deltaY < 0 ? 0 : deltaY
        //
        // newH -= deltaY < 0 ? Math.abs(deltaY) : 0
        //
        // if (newY + newH > this.stageSize.height) {
        //   newH = this.stageSize.height - newY
        // }

        let x = this.imageGroupBound.x
        let y = this.imageGroupBound.y
        let width = this.imageGroupBound.width
        let height = this.imageGroupBound.height

        let newX = x

        if (x < 0) newX = 0

        let newY = y
        if (y < 0) newY = 0

        let newW = width
        if (x < 0) newW = width + x
        if (newX + newW > this.stageSize.width) newW = this.stageSize.width - newX


        let newH = height
        if (y < 0) newH = height + y
        if (newY + newH > this.stageSize.height) newH = this.stageSize.height - newY

        return {
          show: this.isScrawl || this.isClip,
          x: newX,
          y: newY,
          width: newW,
          height: newH
        }
      },
      stageConfig() {
        return {
          scale: {x: this.scale, y: this.scale},
          width: this.stageSize.width,
          height: this.stageSize.height,
          offsetX: -this.stageSize.width / this.scale / 2
        }
      },
      allHeight() {
        return this.dataList.length * this.imageSize.height
      },
      currPageIndex() {
        let p = (this.scrollTop + this.stageSize.height / this.scale / 2) / this.allHeight
        let currPageIndex = Math.floor(p * this.dataList.length) || 0
        return currPageIndex
      },
      dataShowList() {
        let pageCount = this.pageCount % 2 ? this.pageCount : this.pageCount + 1
        let start = Math.ceil(this.currPageIndex - pageCount / 2)
        let end = start + pageCount - 1
        let arr = this.dataList.filter((item, index) => index >= start && index <= end)
        // if (this.$refs.stage) {
        //   let stage = this.$refs.stage.getStage()
        //   let layer = stage.findOne('Layer')
        //   console.log(layer.children)
        // }
        return arr
      },
      minScale() {
        return Math.min(this.stageSize.width / this.imageSize.width, this.stageSize.height / this.imageSize.height)
      },
      scale() {
        let scaleType = this.scaleType

        let scale = 1
        if (scaleType === SCALETYPE.pageActual) {
          scale = 1
        } else if (scaleType === SCALETYPE.pageWidth) {
          scale = this.stageSize.width / this.imageSize.width
        } else if (typeof scaleType === 'number') {
          scale = scaleType
        } else if (scaleType === SCALETYPE.pageFit) {
          scale = Math.min(this.stageSize.width / this.imageSize.width, this.stageSize.height / this.imageSize.height)
        }
        return scale || 1
      },
      scaleType: {
        get() {
          return this.privateData.scaleType
        },
        set(val) {
          if (this.maskBound.show) return
          if (typeof val === 'number') {
            if (val < this.minScale) val = this.minScale
            else if (val > this.maxScale) val = this.maxScale
          }

          if (this.wholePage) val = SCALETYPE.pageFit

          this.privateData.scaleType = val
        }
      },
      scrollTop: {
        get() {
          return this.privateData.scrollTop
        },
        set(val) {
          if (val < 0) val = 0
          else if (val > this.scrollHeight) val = this.scrollHeight
          this.privateData.scrollTop = val
        }
      },
      scrollLeft: {
        get() {
          return this.privateData.scrollLeft
        },
        set(val) {
          let min = -this.scrollWidth / 2
          let max = this.scrollWidth / 2
          if (val < min) val = min
          else if (val > max) val = max
          this.privateData.scrollLeft = val
        }
      },
      scrollHeight() {
        return this.dataList.length * this.imageSize.height - this.stageSize.height / this.scale
      },
      scrollWidth() {
        let scrollWidth = (this.dataList.length ? 1 : 0) * this.imageSize[this.rotation % 180 ? 'height' : 'width'] - this.stageSize.width / this.scale

        return scrollWidth < 0 ? 0 : scrollWidth
      },
      rotation: {
        get() {
          return this.privateData.rotation
        },
        set(val) {
          if (this.maskBound.show) return
          if (this.wholePage) val = 0
          this.privateData.rotation = (val + 360) % 360
        }
      },
      progress() {
        return this.scrollTop / this.scrollHeight
      },
      scrollbarVThumbStyle() {
        let hp = this.stageSize.height / this.scale / this.dataList.length / this.imageSize.height * 100
        if (this.$refs.vScrollbar) {
          let pH = this.$refs.vScrollbar.offsetHeight
          let h = pH * hp / 100
          if (h < 17) hp = 17 * 100 / pH
        }
        return {
          height: hp + '%',
          top: this.progress * (100 - hp) + '%',
          backgroundColor: this.scrollbarConfig.backgroundColor || '#c1c1c1',
          borderRadius: this.scrollbarConfig.borderRadius || 0
        }
      },
      scrollbarHThumbStyle() {
        let hp = this.stageSize.width / this.scale / this.imageSize[this.rotation % 180 ? 'height' : 'width'] * 100
        if (this.$refs.hScrollbar) {
          let pH = this.$refs.hScrollbar.offsetWidth
          let h = pH * hp / 100
          if (h < 17) hp = 17 * 100 / pH
        }
        return {
          width: hp + '%',
          left: (this.scrollLeft + this.scrollWidth / 2) / this.scrollWidth * (100 - hp) + '%',
          backgroundColor: this.scrollbarConfig.backgroundColor || '#c1c1c1',
          borderRadius: this.scrollbarConfig.borderRadius || 0
        }
      }
    },
    mounted() {
      this.loaded = true

      let container = this.$refs.container

      this.hm = new Hammer.Manager(container)
      this.hm.add(new Hammer.Tap())
      this.hm.add(new Hammer.Pan())
      this.hm.add(new Hammer.Pinch()).recognizeWith([this.hm.get('pan')])
      this.hm.on('pinchstart pinchmove', this.onPinch)
      this.hm.on('panstart', this.onPan)
      this.hm.on('tap', this.onTap)
    },
    beforeDestroy() {
      if (this.hm) this.hm.destroy()
    },
    methods: {
      onPinch(ev) {
        if (ev.type === 'pinchstart') this.oldScale = this.scale
        this.scaleType = 0.9 * ev.scale * this.oldScale
      },
      onTap() {
        this.clearSelection()
        this.stopScroll()
      },
      onPan(evt) {
        this.stopScroll()
        if (evt.type === 'panstart') evt = evt.changedPointers[0]
        this.clearSelection()
        if (this.isScrawl) {
          this.handleScrawl(evt)
          return
        }
        if (this.isClip) {
          this.handleClip(evt)
          return
        }
        if (this.pointerType === POINTERTYPE.text) {
          this.handleSelectText(evt)
        } else if (this.pointerType === POINTERTYPE.hand) {
          this.handleMoveImage(evt)
        }

      },
      async handleLeftMousedown(evt) {
        // if (evt.type === 'touchstart') {
        //   evt = evt.targetTouches[0]
        // }
        // this.clearSelection()
        // if (this.isScrawl) {
        //   this.handleScrawl(evt)
        //   return
        // }
        // if (this.isClip) {
        //   this.handleClip(evt)
        //   return
        // }
        // if (this.pointerType === 'text') {
        //   this.handleSelectText(evt)
        // } else if (this.pointerType === 'hand') {
        //   this.handleMoveImage(evt)
        // }
        //
      },
      sendEvent() {
        this.$emit(...arguments)
      },
      test() {
        // console.log(this.getStage().children[0].children[0].children[0].children[3].toJSON())
        // console.log(JSON.parse('{"attrs":{"x":122.7057,"y":70.690025,"width":114.04183000000002,"height":31.5,"fill":"rgba(255,0,0,0.3)"},"className":"Rect"}'))
      },
      setPointerType(pointerType) {
        this.pointerType = POINTERTYPE[pointerType] || POINTERTYPE.text
      },
      addScrawlData(scrawlData) {
        let index = this.scrawlList.findIndex(scrawlItem => scrawlItem.pageIndex === scrawlData.pageIndex)

        if (index > -1) this.scrawlList.splice(index, 1)

        this.scrawlList.push(scrawlData)
      },
      setScrawlData(scrawlList) {
        this.scrawlList = scrawlList
      },
      removeMark(markid) {
        let index = this.markList.findIndex(item => item.markid === markid)
        if (index > -1) this.markList.splice(index, 1)
      },
      prevSearchResult() {
        this.seachResultIndex--
      },
      nextSearchResult() {
        this.seachResultIndex++
      },
      async search(str) {
        if (this.maskBound.show) return
        this.searchBoxShow = true
        this.searchResultList = []
        this.seachResultIndex = 0
        this.searchText = str
        this.logger.info('开始搜索,关键字：', str)
        this.searchLoading = true
        let strs = str.replace(/\s/g, '').split('')
        let allData = []
        let dataArr = []
        for (let pageIndex = 0; pageIndex < this.dataList.length; pageIndex++) {
          let page = this.dataList[pageIndex]
          let text = await page.text()
          if (!text) {
            dataArr = []
            continue
          }
          let words = text.words
          if (!words) {
            dataArr = []
            continue
          }
          for (let wordIndex = 0; wordIndex < words.length; wordIndex++) {
            let word = words[wordIndex]
            if (word.text === strs[dataArr.length]) {
              dataArr.push({
                ...word,
                index: wordIndex,
                pageIndex: pageIndex
              })
              if (dataArr.length === strs.length) {
                allData.push(dataArr)
                dataArr = []
              }
            } else if (!/\s/.test(word.text)) dataArr = []
          }
        }
        this.logger.info('搜索结束，共搜索到：', allData.length, '个结果')
        this.searchResultList = allData
        this.searchLoading = false
        let seachResultIndex = 1
        for (let i = 0; i < allData.length; i++) {
          let searchResultItem = allData[i]
          let A = searchResultItem[0]
          let B = searchResultItem[searchResultItem.length - 1]
          if (this.currPageIndex >= A.pageIndex) {
            seachResultIndex = i + 1
            break
          }
        }
        if (seachResultIndex > allData.length) seachResultIndex = allData.length
        this.seachResultIndex = seachResultIndex
      },
      exitSearch() {
        this.searchBoxShow = false
      },
      toggleSearchBoxShow(onlyOpen = false) {
        if (this.maskBound.show) return
        if (typeof onlyOpen !== 'boolean') onlyOpen = false
        this.searchBoxShow = onlyOpen ? true : !this.searchBoxShow
      },
      clip() {
        if (this.maskBound.show) return
        this.isClip = true
      },
      exitClip(isComplete = false) {
        if (!this.isClip) return
        if (typeof isComplete !== 'boolean') isComplete = false
        if (isComplete) {
          let clipImage = this.getStage().findOne('#clipImage')
          this.$emit('clip-complete', {
            pageIndex: this.currPageIndex,
            x: this.clipData.x,
            y: this.clipData.y,
            width: this.clipData.width,
            height: this.clipData.height,
            dataURL: clipImage.toDataURL()
          })
        }
        this.clipData.x = 0
        this.clipData.y = 0
        this.clipData.width = 1
        this.clipData.height = 1
        this.isClip = false
        this.hideClipTool()
      },
      showScrawl() {
        this.scrawlShow = true
      },
      hideScrawl() {
        this.scrawlShow = false
      },
      showMark() {
        this.markShow = true
      },
      hideMark() {
        this.markShow = false
      },
      scrawl() {
        if (this.maskBound.show) return
        this.isScrawl = true
      },
      exitScrawl() {
        this.isScrawl = false
      },
      handleVScollbarMousedown(evt) {
        let startY = evt.screenY
        let startTop = this.scrollTop
        let hp = this.stageSize.height / this.scale / this.dataList.length / this.imageSize.height * 100
        const move = (ev) => {
          let deltaY = ev.screenY - startY
          this.scrollTo({y: startTop + deltaY / this.scale / (hp / 100)})
        }
        const up = () => {
          removeEventListener("mousemove", move)
          removeEventListener("mouseup", up)
        }
        addEventListener("mousemove", move)
        addEventListener("mouseup", up)
      },
      handleHScollbarMousedown(evt) {
        let startX = evt.screenX
        let startLeft = this.scrollLeft
        let hp = this.stageSize.width / this.scale / this.imageSize.width * 100

        const move = (ev) => {

          let deltaX = ev.screenX - startX
          this.scrollTo({x: startLeft + deltaX / this.scale / (hp / 100)})
        }
        const up = () => {
          removeEventListener("mousemove", move)
          removeEventListener("mouseup", up)
        }
        addEventListener("mousemove", move)
        addEventListener("mouseup", up)
      },
      handleClip(evt) {
        let cursor = this.cursor
        let clipData = JSON.parse(JSON.stringify(this.clipData))
        this.hideClipTool()
        let startPointerPositionOnImageGroup = this.getPointerPositionOnImageGroup(evt)
        const move = async (ev) => {
          let pointerPositionOnImageGroup = this.getPointerPositionOnImageGroup(ev, true)
          let deltaX = pointerPositionOnImageGroup.x - startPointerPositionOnImageGroup.x
          let deltaY = pointerPositionOnImageGroup.y - startPointerPositionOnImageGroup.y
          if (!cursor) {
            this.clipData.x = startPointerPositionOnImageGroup.x
            this.clipData.y = startPointerPositionOnImageGroup.y
            this.clipData.width = deltaX
            this.clipData.height = deltaY
          } else {
            // console.log(pointerPositionOnImageGroup, cursor)
            if (cursor === 'left-top') {
              this.clipData.x = clipData.x + deltaX
              this.clipData.y = clipData.y + deltaY
              this.clipData.width = clipData.width - deltaX
              this.clipData.height = clipData.height - deltaY
            } else if (cursor === 'left-middle') {
              this.clipData.x = clipData.x + deltaX
              this.clipData.width = clipData.width - deltaX
            } else if (cursor === 'left-bottom') {
              this.clipData.x = clipData.x + deltaX
              this.clipData.width = clipData.width - deltaX
              this.clipData.height = clipData.height + deltaY
            } else if (cursor === 'center-top') {
              this.clipData.y = clipData.y + deltaY
              this.clipData.height = clipData.height - deltaY
            } else if (cursor === 'center-bottom') {
              this.clipData.height = clipData.height + deltaY
            } else if (cursor === 'right-top') {
              this.clipData.y = clipData.y + deltaY
              this.clipData.width = clipData.width + deltaX
              this.clipData.height = clipData.height - deltaY
            } else if (cursor === 'right-middle') {
              this.clipData.width = clipData.width + deltaX
            } else if (cursor === 'right-bottom') {
              this.clipData.width = clipData.width + deltaX
              this.clipData.height = clipData.height + deltaY
            }
          }
        }
        const up = () => {
          removeEventListener("mousemove", move)
          removeEventListener("mouseup", up)
          // window.removeEventListener("touchmove", move)
          // window.removeEventListener("touchend", up)
          this.showClipTool()
        }
        addEventListener("mousemove", move)
        addEventListener("mouseup", up)
      },
      setScrawlType(scrawlType) {
        this.scrawlType = SCRAWLTYPE[scrawlType] || SCRAWLTYPE.Line
      },
      handleScrawl(evt) {
        let startPointerPosition = this.getPointerPositionOnImageGroup(evt)
        let shape = {
          attrs: {
            strokeWidth: this.brushWidth,
            stroke: this.brushColor,
            id: uuid(),
            name: 'scrawl'
          }
        }
        if (this.scrawlType === SCRAWLTYPE.Line) {
          shape.className = 'Line'
          shape.attrs.points = [startPointerPosition.x, startPointerPosition.y]
        } else if (this.scrawlType === SCRAWLTYPE.Rect) {
          shape.className = 'Rect'
          shape.attrs.x = startPointerPosition.x
          shape.attrs.y = startPointerPosition.y
          shape.attrs.width = 1
          shape.attrs.height = 1
        } else if (this.scrawlType === SCRAWLTYPE.Ellipse) {
          shape.className = 'Ellipse'
          shape.attrs.x = startPointerPosition.x
          shape.attrs.y = startPointerPosition.y
          shape.attrs.width = 1
          shape.attrs.height = 1
        } else if (this.scrawlType === SCRAWLTYPE.Arrow) {
          shape.className = 'Arrow'
          shape.attrs.points = [startPointerPosition.x, startPointerPosition.y]
          shape.attrs.pointerWidth = this.brushWidth * 1.5
          shape.attrs.pointerLength = this.brushWidth * 2.5
        }
        if (this.scrawlType === SCRAWLTYPE.Eraser) {
          let shape = this.getStage().getIntersection(this.getPointerPositionOnStage(evt))
          if (shape && shape.hasName('scrawl')) {
            let id = shape.id()
            this.removeScrawl(id, this.currPageIndex)
          }
        } else {
          let scrawlItem = this.scrawlList.find(item => item.pageIndex === this.currPageIndex)
          if (!scrawlItem) {
            scrawlItem = {pageIndex: this.currPageIndex, data: []}
            this.scrawlList.push(scrawlItem)
          }
          scrawlItem.data.push(shape)
        }
        const move = async (ev) => {
          this.setScrawlToolPosition()

          if (this.scrawlType === SCRAWLTYPE.Eraser) {
            let shape = this.getStage().getIntersection(this.getPointerPositionOnStage(ev))
            if (shape && shape.hasName('scrawl')) {
              let id = shape.id()
              this.removeScrawl(id, this.currPageIndex)
            }
            return
          }

          let pointerPositionOnImageGroup = this.getPointerPositionOnImageGroup(ev, true)
          if (this.scrawlType === SCRAWLTYPE.Line) {
            let p = shape.attrs.points.concat(pointerPositionOnImageGroup.x, pointerPositionOnImageGroup.y)
            this.$set(shape.attrs, 'points', p)
            this.$set(shape.attrs, 'hitStrokeWidth', shape.attrs.strokeWidth + 10)
          } else if (this.scrawlType === SCRAWLTYPE.Rect) {
            shape.attrs.width = pointerPositionOnImageGroup.x - shape.attrs.x
            shape.attrs.height = pointerPositionOnImageGroup.y - shape.attrs.y
          } else if (this.scrawlType === SCRAWLTYPE.Ellipse) {
            let x = Math.min(pointerPositionOnImageGroup.x, startPointerPosition.x)
            let y = Math.min(pointerPositionOnImageGroup.y, startPointerPosition.y)
            shape.attrs.width = Math.abs(pointerPositionOnImageGroup.x - startPointerPosition.x)
            shape.attrs.height = Math.abs(pointerPositionOnImageGroup.y - startPointerPosition.y)
            shape.attrs.x = x + shape.attrs.width / 2
            shape.attrs.y = y + shape.attrs.height / 2
          } else if (this.scrawlType === SCRAWLTYPE.Arrow) {
            let p = [startPointerPosition.x, startPointerPosition.y].concat(pointerPositionOnImageGroup.x, pointerPositionOnImageGroup.y)
            this.$set(shape.attrs, 'points', p)
          }
        }
        const up = () => {
          removeEventListener("mousemove", move)
          removeEventListener("mouseup", up)
          // window.removeEventListener("touchmove", move)
          // window.removeEventListener("touchend", up)
        }
        addEventListener("mousemove", move)
        addEventListener("mouseup", up)
      },
      handleMoveImage(evt) {
        let startPointerPositionOnStage = this.getPointerPositionOnStage(evt)
        let oldScrollTop = this.scrollTop
        let oldScrollLeft = this.scrollLeft
        this.isGrabbing = true
        let deltaY = 0
        let deltaX = 0
        let prevTime = Date.now()
        let prevScreenY = evt.screenY
        let prevScreenX = evt.screenX
        let yv = 0
        let xv = 0
        const move = async (ev) => {
          // if (ev.type === 'touchmove') ev = ev.targetTouches[0]

          let pointerPositionOnStage = this.getPointerPositionOnStage(ev)
          deltaY = pointerPositionOnStage.y - startPointerPositionOnStage.y
          deltaX = pointerPositionOnStage.x - startPointerPositionOnStage.x

          if (this.wholePage) return
          let now = Date.now()
          let movementY = ev.screenY - prevScreenY
          let movementX = ev.screenX - prevScreenX
          prevScreenY = ev.screenY
          prevScreenX = ev.screenX
          yv = movementY / (now - prevTime)
          xv = movementX / (now - prevTime)
          prevTime = now

          this.scrollTo({
            x: oldScrollLeft - deltaX / this.scale,
            y: oldScrollTop - deltaY / this.scale
          })
        }
        const up = () => {
          this.isGrabbing = false
          removeEventListener("mousemove", move)
          removeEventListener("mouseup", up)
          // window.removeEventListener("touchmove", move)
          // window.removeEventListener("touchend", up)
          this.autoScrollView(yv, xv)
          if (!this.wholePage) return
          if (Math.abs(deltaY) > 50) {
            if (deltaY > 0) {
              this.prevPage()
            } else {
              this.nextPage()
            }
          }
        }
        addEventListener("mousemove", move)
        addEventListener("mouseup", up)
      },
      stopScroll() {
        if (this.autoScrollViewing) clearTimeout(this.autoScrollViewing)
      },
      // v0=(Math.spr(x+0.0025)-0.05)/10
      autoScrollView(yv0 = 0, xv0 = 0) {
        this.log('yv0：', yv0, 'xv0：', xv0)
        let ya = 0.005 * (yv0 > 0 ? -1 : 1)
        let xa = 0.005 * (xv0 > 0 ? -1 : 1)
        let startTime = Date.now()
        const getPlusMinus = n => {
          if (Number(n) > 0) return 1
          else if (Number(n) < 0) return -1
          else if (Number(n) === 0) return 0
        }

        let scrollTop = this.scrollTop
        let scrollLeft = this.scrollLeft

        let ys = 0
        let xs = 0
        let oldYs = 0
        let oldXs = 0
        const fn = () => {
          this.autoScrollViewing = setTimeout(() => {
            let t = Date.now() - startTime
            let yv = yv0 + ya * t
            let xv = xv0 + xa * t
            if (getPlusMinus(yv) === getPlusMinus(yv0)) {
              oldYs = ys
              ys = yv0 * t + 0.5 * ya * t * t
            }
            if (getPlusMinus(xv) === getPlusMinus(xv0)) {
              oldXs = xs
              xs = xv0 * t + 0.5 * xa * t * t
            }

            // if ((oldXs === xs) && (oldYs === ys)) return

            if ((getPlusMinus(yv) !== getPlusMinus(yv0) && getPlusMinus(xv) !== getPlusMinus(xv0))) return
            //
            // let xs = xv0 * t + 0.5 * xa * t * t
            this.scrollTo({
              y: scrollTop - ys / this.scale,
              x: scrollLeft - xs / this.scale
            })
            this.log('yv：', yv, 'ys：', ys, 'xs：', xs)
            fn()
          }, 17)
        }
        fn()
      },
      async handleSelectText(evt) {
        let A = await this.getWordByPointerPosition(evt)
        const move = async (ev) => {
          // if (ev.type === 'touchmove') ev = ev.targetTouches[0]

          let pointerPositionOnStage = this.getPointerPositionOnStage(ev)
          let B = await this.getWordByPointerPosition(ev)
          if (!B) return
          if (!A) A = B

          let pos = await this.getPos(A, B)
          if (pos === null) return

          this.addMark({
            markid: 'selection-mark',
            start: pos.start,
            end: pos.end,
            config: {
              stroke: '',
              strokeWidth: 0,
              fill: this.selectionBgColor,
              name: 'selection'
            }
          })
          if (this.wholePage) return
          if (pointerPositionOnStage.y + 30 > this.stageSize.height) {
            this.scrollBy({y: 15 / this.scale})
          } else if (pointerPositionOnStage.y - 30 < 0) {
            this.scrollBy({y: -15 / this.scale})
          }
          if (pointerPositionOnStage.x + 50 > this.stageSize.width) {
            this.scrollBy({x: 15 / this.scale})
          } else if (pointerPositionOnStage.x - 50 < 0) {
            this.scrollBy({x: -15 / this.scale})
          }
        }
        const up = () => {
          removeEventListener('mousemove', move)
          removeEventListener('mouseup', up)
          // window.removeEventListener("touchmove", move)
          // window.removeEventListener("touchend", up)

          let selection = this.getSelection()
          if (selection) this.$emit('selection-change', selection)
        }
        addEventListener('mousemove', move)
        addEventListener('mouseup', up)
      },

      handleWheel(ev) {
        this.stopScroll()
        if (this.maskBound.show) return
        let wheelDeltaY = ev.wheelDeltaY
        if (this.wholePage) {
          this.skipPage(this.currPageIndex - wheelDeltaY / Math.abs(wheelDeltaY))
        } else {
          if (ev.ctrlKey) {
            ev.preventDefault()
            if (wheelDeltaY > 0) {
              this.zoomin()
            } else if (wheelDeltaY < 0) {
              this.zoomout()
            }
          } else {
            this.scrollBy({y: -wheelDeltaY / this.scale})
          }
        }
      },
      getLayer() {
        let stage = this.getStage()
        return stage.findOne('Layer')
      },
      getStage() {
        return this.$refs.stage.getStage()
      },

      setInitConfig() {
        let initConfig = JSON.parse(JSON.stringify(this.initConfig))

        if (initConfig.markShow === undefined) initConfig.markShow = false
        if (initConfig.scrawlShow === undefined) initConfig.scrawlShow = false
        if (initConfig.isExpandScrawlTool === undefined) initConfig.isExpandScrawlTool = false
        if (initConfig.scrollTop === undefined) initConfig.scrollTop = 0
        if (initConfig.scrollLeft === undefined) initConfig.scrollLeft = 0
        if (initConfig.scaleType === undefined) initConfig.scaleType = SCALETYPE.pageFit
        if (initConfig.rotation === undefined) initConfig.rotation = 0
        if (initConfig.pointerType === undefined) initConfig.pointerType = POINTERTYPE.text
        if (initConfig.dataList === undefined) initConfig.dataList = []
        if (initConfig.markList === undefined) initConfig.markList = []
        if (initConfig.scrawlList === undefined) initConfig.scrawlList = []

        for (let key in initConfig) {
          this[key] = initConfig[key]
        }
      },

      clearSelection() {
        let index = this.markList.findIndex(item => item.markid === 'selection-mark')
        if (index > -1) this.markList.splice(index, 1)
      },
      handleRightMousedown(evt) {
        if (this.maskBound.show) return
        let shape = this.getStage().getIntersection(this.getPointerPositionOnStage(evt))
        if (!shape || !shape.hasName('selection')) this.clearSelection()
        let selection = this.getSelection()
        this.$emit('contextmenu', selection)
      },
      getSelection() {
        let selection = this.markList.find(item => item.markid === 'selection-mark')

        if (!selection) return null

        return {
          start: selection.start,
          end: selection.end,
          text: selection.text
        }
      },
      showClipTool(ev = event) {
        let clipTool = this.$refs.clipTool
        // let pointerPositionOnStage = this.getPointerPositionOnStage(ev)
        let clipImage = this.getStage().findOne('#clipImage')
        if (!clipImage) return
        let clientRect = clipImage.getClientRect()
        let x = clientRect.x + clientRect.width - clipTool.offsetWidth
        let y = clientRect.y + clientRect.height + 5

        if (y + clipTool.offsetHeight > this.stageSize.height) {
          x = clientRect.x
          y = clientRect.y + 10
        }
        if (x < 0) x = 0

        clipTool.style.left = `${x}px`
        clipTool.style.top = `${y}px`
        clipTool.style.visibility = `visible`
      },
      hideClipTool() {
        let clipTool = this.$refs.clipTool
        clipTool.style.visibility = `hidden`
      },
      removeScrawl(id, pageIndex) {
        let scrawlItem = null
        if (pageIndex === undefined) {
          scrawlItem = this.scrawlList.find(item => item.data.some(_item => _item.attrs.id === id))
        } else {
          scrawlItem = this.scrawlList.find(item => item.pageIndex === pageIndex)
        }
        let index = scrawlItem.data.findIndex(_item => _item.attrs.id === id)
        if (index > -1) scrawlItem.data.splice(index, 1)
      },
      setScrawlToolPosition() {
        let ev = event
        let pointerPositionOnStage = this.getPointerPositionOnStage(ev)
        let cx = pointerPositionOnStage.x
        let cy = pointerPositionOnStage.y
        if (cx >= this.maskBound.x && cx <= this.maskBound.x + this.$refs.scrawlTool.offsetWidth &&
          cy >= this.maskBound.y + this.maskBound.height - this.$refs.scrawlTool.offsetHeight && cy <= this.maskBound.y + this.maskBound.height
        ) {
          // this.scrawlToolPosition.x = this.maskBound.x
          // this.scrawlToolPosition.y = this.maskBound.y
          this.isOverScrawlTool = true
        } else {
          this.isOverScrawlTool = false
          // this.scrawlToolPosition.x = this.maskBound.x
          // this.scrawlToolPosition.y = this.maskBound.y + this.maskBound.height - this.$refs.scrawlTool.offsetHeight
        }
      },
      async getPos(A, B) {
        if (A.pageIndex > B.pageIndex) {
          [A, B] = [B, A]
        } else if (A.pageIndex === B.pageIndex) {
          if (A.index > B.index) {
            [A, B] = [B, A]
          } else if (A.index === B.index) {
            return null
          }
        }
        let start = `${A.pageIndex}-${A.index}`
        let end = `${B.pageIndex}-${B.index}`
        if (A.position === 'right') {
          let data = this.dataList[A.pageIndex]
          let text = await data.text()
          if (!text.words) return null
          if (A.index === text.words.length - 1) {
            // 最后一个字右边
            return null
          }
          start = `${A.pageIndex}-${A.index + 1}`
        }
        if (B.position === 'left') {
          if (B.index === 0) {
            // 第一个字左边
            return null
          }
          end = `${B.pageIndex}-${B.index - 1}`
        }

        return {start, end}
      },
      async open(...arg) {
        this.isOpened = false
        // {data, scrawlList = [], markList = []}

        this.setInitConfig()
        this.loadArg = arg
        if (this.loadFn) {
          if (typeof this.loadFn !== 'function') throw new Error('参数 load-fn 必须为一个方法')
          arg = await this.loadFn(...arg)
        } else {
          arg = arg[0]
        }
        let data = []
        let markList = []
        let scrawlList = []
        if (arg && typeof arg === 'object') {
          data = arg.data || []
          markList = arg.markList || []
          scrawlList = arg.scrawlList || []
        }
        this.dataList = data.map((item, index) => {
          return {
            ...item,
            pageIndex: index,
            img: () => this.getImage(item, index),
            text: () => this.getText(item, index)
          }
        })
        markList.map(item => {
          let $item = JSON.parse(JSON.stringify(item))
          if (!$item.config) $item.config = {}
          this.addMark({
            start: item.start,
            end: item.end,
            config: {
              ...$item.config,
              name: 'mark'
            }
          })
        })
        this.scrawlList = scrawlList.map(item => ({...item}))
        this.isOpened = true
        this.$emit('opened')
      },
      getImage(item, index) {
        if (this.imageLoadFn) {
          if (typeof this.imageLoadFn !== 'function') return reject(new Error('参数 image-load-fn 必须为一个方法'))
          this.logger.info(`即将开始加载 ${index + 1} 页的图片数据`)
          return this.imageLoadFn(item.img, index)
        }
        return item.img
      }
    },
    props: {
      textLoadFn: Function,
      loadFn: Function,
      imageLoadFn: Function,
      debugger: Boolean,
      wholePage: Boolean,
      shadowConfig: {
        type: Object,
        default: () => ({})
      },
      selectionBgColor: {
        type: String,
        default: 'rgba(0,180,255,0.5)'
      },
      scrollbarConfig: {
        type: Object,
        default: () => {
          return {
            width: '17px',
            height: '17px',
            backgroundColor: '#c1c1c1',
            borderRadius: 0
          }
        }
      },
      initConfig: {
        type: Object,
        default: () => {
          return {}
        }
      },
      maxScale: {
        type: Number,
        default: 4
      },
      toolboxHeight: {
        type: String,
        default: '30px'
      },
      pageCount: {
        type: Number,
        default: 5
      },
      watermark: {
        type: Object,
        default: () => {
          return null
        }
      },
    }
  }
</script>

<style scoped lang="scss">
  * {
    user-select: none;
  }

  .pdf-marking-out-box {
    height: 100%;
    width: 100%;

    .pdf-marking-in-box {
      height: 100%;
      width: 100%;
      display: flex;
      flex-direction: column;

      .tool-out-box {
        /*height: 100%;*/
        width: 100%;
        overflow: hidden;
        flex-shrink: 0;
        display: flex;
        align-items: center;
        overflow-x: auto;

        & > * {
          flex-shrink: 0;
        }

        /*.tool-box {*/
        /*  height: 100%;*/
        /*  width: 100%;*/
        /*  background-color: #eee;*/
        /*  display: flex;*/
        /*  flex-direction: row;*/
        /*  align-items: center;*/

        .tool-box-item {
          /*display: inline-block;*/
          height: 30px;
          width: 30px;
          margin-left: 10px;
          background-repeat: no-repeat;
          background-position: center;
          background-size: 20px;
          border-radius: 5px;

          &:nth-child(1) {
            margin-left: 0;
          }

          &.search {
            background-image: url("../../../assets/img/search.png");
          }

          &.zoomin {
            background-image: url("../../../assets/img/zoomin.png");
          }

          &.zoomout {
            background-image: url("../../../assets/img/zoomout.png");
          }

          &.rotatecw {
            background-image: url("../../../assets/img/rotatecw.png");
          }

          &.rotateccw {
            background-image: url("../../../assets/img/rotateccw.png");
          }

          &.pointertype {
            background-image: url("../../../assets/img/pointertype.png");
          }

          &.scrawl {
            background-image: url("../../../assets/img/line_black.png");
          }

          &.scrawlshow {
            background-image: url("../../../assets/img/scrawlshow.png");
          }

          &.markshow {
            background-image: url("../../../assets/img/markshow.png");
          }

          &.active {
            background-color: #ccc;
          }

          &:hover {
            background-color: #ccc;
          }
        }

        /*}*/
      }

      .container-out-box {
        background-color: #ddd;
        position: relative;
        overflow: hidden;
        flex: 1;

        .container-box {
          /*height: 100%;*/
          outline: none;

          /deep/ .konvajs-content {
            width: 100% !important;
            height: 100% !important;
          }
        }

        .scrollbar {
          position: absolute;
          overflow: hidden;
          background-color: #f1f1f1;
          box-sizing: border-box;

          & .scrollbar-thumb {
            position: absolute;
            background-color: #c1c1c1;
            opacity: .7;
            min-height: 17px;
            min-width: 17px;
            box-sizing: border-box;

            &:hover {
              opacity: 1;
            }
          }

          &.v {
            width: 17px;
            right: 0;
            top: 0;
            bottom: 0;
            overflow: visible;

            & .scrollbar-thumb {
              width: 100%;
              /*height: 50px;*/
              /*top: 50%;*/
            }

            &::after {
              position: absolute;
              content: '';
              width: 17px;
              height: 17px;
              top: 100%;
              background-color: #c1c1c1;
              opacity: .7;
            }
          }

          &.h {
            height: 17px;
            right: 0;
            left: 0;
            bottom: 0;

            & .scrollbar-thumb {
              height: 100%;
            }
          }
        }

        .mask-box {
          position: absolute;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          /*background-color: rgba(0, 0, 0, 0.3);*/
          pointer-events: none;
          overflow: hidden;

          & > div {
            position: absolute;
            background-color: rgba(0, 0, 0, 0.5);
            pointer-events: all;

            &.mask-top {
              top: 0;
              left: 0;
            }

            &.mask-right {
              top: 0;
              right: 0;
            }

            &.mask-bottom {
              bottom: 0;
              right: 0;
            }

            &.mask-left {
              bottom: 0;
              left: 0;
            }
          }
        }

        $scrawl_tool_height: 50px;

        .scrawl-tool {
          position: absolute;
          z-index: 999999999999;
          height: $scrawl_tool_height;
          background-color: rgba(204, 204, 204, 0.3);
          border-radius: 3px;
          cursor: default !important;
          /*pointer-events: all;*/
          visibility: hidden;

          .expand {
            width: unset;
          }

          &:hover {
            background-color: #cccccc;
          }

          &.show {
            visibility: visible;
          }

          .scrawl-tool-item {
            display: inline-block;
            width: calc(#{$scrawl_tool_height} - 10px);
            height: calc(#{$scrawl_tool_height} - 10px);
            margin-left: 5px;
            margin-top: 5px;
            border-radius: 5px;
            cursor: pointer !important;
            background-size: calc(#{$scrawl_tool_height} - 20px);
            background-position: center;
            background-repeat: no-repeat;
            vertical-align: top;
            padding: 5px;
            box-sizing: border-box;

            &:nth-last-child(1) {
              margin-right: 5px;
            }

            input[type=color] {
              width: 100%;
              height: 100%;
              border: none;
              vertical-align: top;
              -webkit-appearance: none;
              text-align: center;
              display: inline-block;
              background-color: transparent;
              outline: none;
              padding: 0;
            }

            select {
              width: 100%;
              height: 100%;
              border: none;
              vertical-align: top;
              -webkit-appearance: none;
              display: inline-block;
              background-color: transparent;
              outline: none;
              padding: 0;
              text-align: center;
              text-align-last: center;
            }

            &.line {
              background-image: url("../../../assets/img/line_black.png");
            }

            &.rect {
              background-image: url("../../../assets/img/rect_black.png");
            }

            &.ellipse {
              background-image: url("../../../assets/img/ellipse_black.png");
            }

            &.arrow {
              background-image: url("../../../assets/img/arrow_black.png");
            }

            &.eraser {
              background-image: url("../../../assets/img/eraser_black.png");
            }

            &.color {
            }

            &.width {
            }

            &.exit {
              background-image: url("../../../assets/img/exit.png");
            }

            &.expand {
              background-image: url("../../../assets/img/arrow_right.png");
            }

            &.collapse {
              background-image: url("../../../assets/img/arrow_left.png");
            }

            &:hover {
              background-color: #fff;
            }

            &.active {
              background-color: #fff;
            }

          }
        }

        $clip_tool_height: 30px;

        .clip-tool {
          height: $clip_tool_height;
          position: absolute;
          z-index: 999999999999;
          top: 0;
          left: 0;
          background-color: #cccccc;
          border-radius: 3px;
          font-size: 0;
          overflow: hidden;
          visibility: hidden;

          .clip-tool-item {
            display: inline-block;
            font-size: 12px;
            line-height: $clip_tool_height;
            height: $clip_tool_height;
            min-width: $clip_tool_height;
            text-align: center;
            padding: 0 10px;

            &:hover {
              background-color: white;
            }
          }
        }

        .marking-search-box {
          position: absolute;
          top: 20px;
          right: 30px;
          height: 30px;
          border: 1px solid black;
          border-radius: 15px;
          overflow: hidden;
          padding: 0 15px;
          font-size: 0;
          background-color: white;

          .input {
            display: inline-block;
            height: 100%;
            position: relative;

            input {
              border: none;
              outline: none;
              box-sizing: border-box;
              height: 100%;
              font-size: 14px;
              vertical-align: top;
              padding-right: 60px;
              cursor: text;
            }

            .search-result {
              font-size: 14px;
              line-height: 30px;
              vertical-align: top;
              margin-left: 5px;
              position: absolute;
              right: 0;
            }
          }

          .btn {
            display: inline-block;
            font-size: 14px;
            height: 20px;
            width: 20px;
            margin-top: 5px;
            background-size: contain;
            background-repeat: no-repeat;
            background-position: center;
            vertical-align: top;
            margin-left: 5px;
            border-radius: 5px;

            &.search {
              background-image: url("../../../assets/img/search.png");
            }

            &.searching {
              background-image: url("../../../assets/img/loading.png");
              animation: linear infinite rotate 1.2s;
            }

            @keyframes rotate {
              0% {
                transform: rotate(0);
              }
              50% {
                transform: rotate(180deg);
              }
              100% {
                transform: rotate(360deg);
              }
            }

            &.prev {
              background-image: url("../../../assets/img/arrow_left.png");
            }

            &.next {
              background-image: url("../../../assets/img/arrow_right.png");
            }

            &.exit {
              background-image: url("../../../assets/img/exit.png");
            }

            &:not(.searching):hover {
              background-color: #ccc;
            }

            &.disable {
              pointer-events: none;
            }
          }
        }
      }
    }
  }
</style>
